package environment.dungeons;

import static org.junit.Assert.*;

import org.junit.*;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

import environment.Direction;
import environment.borders.*;
import environment.borders.Void;

import temperature.Temperature;

/**
 * Een testklasse voor de klasse Square.
 * Deze bevat alle testmethoden om setters en speciale mutators
 * te controleren van de klasse 'normale vakje'.
 * 
 * @author 	Nathan Bekaert & Philippe de Potter de ten Broeck
 * @version 1.0
 */
public class NormalSquareTest {
	
	Square square1;
	Square square2;
	
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
	}
	@AfterClass
	public static void tearDownAfterClass() throws Exception {
	}
	@Before
	public void setUp() throws Exception {
		square1 = new NormalSquare();
		square2 = new NormalSquare(Temperature.CELSIUS_0,new BigDecimal(50.00));
	}
	@After
	public void tearDown() throws Exception {
	}
	
	/**
	 * Test de associatie tussen Square en Border:
	 *	Bekeken vanuit een enkel vakje; geen interconnectie tussen vakjes.
	 * 	- Creatie
	 * 	- BuildNewBorder
	 * 	- BreakDownBorder
	 * 	- Square.Terminate
	 * 	- Border.Terminate
	 * 	- 
	 */

	@Test
	public void testAssociationSquareBorders() {
		square2.setTemperature(Temperature.CELSIUS_1);
		square2.setHumidity(BigDecimal.ONE);
		square2.setBorder(new Wall(false), Direction.NORTH);
		square2.setBorder(new Wall(false), Direction.WEST);
		square2.breakBorderDownIn(Direction.FLOOR);
		assertTrue(square1.getTemperature().equals(Temperature.CELSIUS_0));
		assertTrue(square1.getHumidity().compareTo(BigDecimal.TEN)==0);
		assertTrue(square2.getTemperature().equals(Temperature.CELSIUS_1));
		assertTrue(square2.getHumidity().compareTo(BigDecimal.ONE)==0);
		assertTrue(square2.getBorderAt(Direction.FLOOR) instanceof Void);
		assertTrue(square2.getBorderAt(Direction.CEILING) instanceof Void);
		assertTrue(square2.getBorderAt(Direction.NORTH) instanceof Wall);
		assertTrue(square2.getBorderAt(Direction.WEST) instanceof Wall);
		assertTrue(square2.getBorderAt(Direction.EAST) instanceof Void);
		assertTrue(square2.getBorderAt(Direction.SOUTH) instanceof Void);
		
		Border door1 = new Door(false);
		Border door2 = new Door(true);
		square1.buildNewBorderIn(door1, Direction.EAST);
		square2.buildNewBorderIn(door2, Direction.EAST);
		assertTrue(square1.getBorderAt(Direction.EAST) instanceof Door);
		assertTrue(square2.getBorderAt(Direction.EAST) instanceof Door);
		
		square2.breakBorderDownIn(Direction.EAST);
		square2.breakBorderDownIn(Direction.WEST);
		assertTrue(square2.getBorderAt(Direction.EAST) instanceof Void);
		assertTrue(square2.getBorderAt(Direction.WEST) instanceof Void);
		
		square1.terminate();
		assertTrue(square1.isTerminated());
		for(Direction direction: Direction.values())
			assertTrue(square1.getBorderAt(direction) == null);
		assertTrue(door1.isTerminated());
		assertTrue(door1.getFirstSquare() == null);
		assertTrue(door1.getSecondSquare()== null);
		
		Border wall1 = new Wall(true);
		square2.buildNewBorderIn(wall1, Direction.EAST);
		Border wall2 = square2.getBorderAt(Direction.NORTH);
		assertTrue(wall2.hasSquare());
		assertTrue((wall2.getFirstSquare()==square2) || (wall2.getSecondSquare()==square2));
		
		wall2.terminate();
		assertTrue(wall2.isTerminated());
		assertTrue(wall2.getFirstSquare() == null);
		assertTrue(wall2.getSecondSquare()== null);
		assertTrue(square2.getBorderAt(Direction.NORTH) instanceof Void);
		
		wall1.terminate();
		assertTrue(wall1.isTerminated());
		assertTrue(wall1.getFirstSquare() == null);
		assertTrue(wall1.getSecondSquare()== null);
		assertFalse(square2.isTerminated());
		Border newWall = square2.getBorderAt(Direction.EAST);
		assertTrue(newWall instanceof Wall);
		assertFalse(wall1.hasSquare());
		assertFalse(newWall.isTerminated());
		assertTrue(newWall.hasAsSquare(square2));
		assertTrue(((Wall) newWall).isSlippery());
		assertFalse(wall1 == newWall);
	}
	
	@Test
	public void testBreakBorderDownNormalCase() {
		Wall wall = new Wall(false);
		square1.setStateUsed();
		square2.setStateUsed();
		square1.buildNewBorderIn(wall, Direction.EAST);
		square1.setNeighbourInDirection(square2, Direction.EAST);
		assertSame(square2, square1.getNeighbourIn(Direction.EAST));
		assertSame(square1, square2.getNeighbourIn(Direction.WEST));
		square1.breakBorderDownIn(Direction.EAST);
		assertSame(square2, square1.getNeighbourIn(Direction.EAST));
		assertTrue(square1.getBorderAt(Direction.EAST) instanceof Void);
		assertTrue(square2.getBorderAt(Direction.WEST) instanceof Void);
		assertSame(square1.getBorderAt(Direction.EAST), square2.getBorderAt(Direction.WEST));
	}
	
	@Test
	public void testBreakBorderDownInNormalCase() {
		Wall wall = new Wall(false);
		square1.setStateUsed();
		square2.setStateUsed();
		square1.buildNewBorderIn(wall, Direction.EAST);
		square1.setNeighbourInDirection(square2, Direction.EAST);
		assertSame(square2, square1.getNeighbourIn(Direction.EAST));
		square1.breakBorderDownIn(Direction.EAST);
		assertSame(square2, square1.getNeighbourIn(Direction.EAST));
		assertTrue(square1.getBorderAt(Direction.EAST) instanceof Void);
		assertTrue(square2.getBorderAt(Direction.WEST) instanceof Void);
		assertSame(square1.getBorderAt(Direction.EAST), square2.getBorderAt(Direction.WEST));
	}
	
	/**
	 * Test de associatie tussen Square's en Border's:
	 *	De nadruk ligt hierbij tussen meerdere squares.
	 * 	- setSquareInDirection
	 */
	@Test
	public void testComplicatedAssociationSquareBorders(){
		Square square3 = new NormalSquare(Temperature.CELSIUS_1, BigDecimal.TEN, Direction.NORTH, Direction.EAST, Direction.SOUTH);		
		square1.setTemperature(Temperature.CELSIUS_1);
		square1.setBorder(new Wall(false), Direction.NORTH);
		square1.setBorder(new Wall(false), Direction.SOUTH);
		square1.setBorder(new Wall(false), Direction.WEST);
		square2.setHumidity(BigDecimal.ZERO);
		Border door = new Door(false);
		square2.buildNewBorderIn(door, Direction.EAST);
		square1.setStateUsed();
		square2.setStateUsed();
		square3.setStateUsed();
		square1.setNeighbourInDirection(square2, Direction.EAST);
		square2.setNeighbourInDirection(square3, Direction.EAST);
		assertEquals(square1.getNeighbourIn(Direction.EAST),square2);
		assertTrue(square2.getNeighbourIn(Direction.EAST) == square3);
		assertTrue(square1.getBorderAt(Direction.EAST) instanceof Void);
		assertTrue(square2.getBorderAt(Direction.WEST) instanceof Void);
		assertTrue(square2.getBorderAt(Direction.EAST) instanceof Door);
		assertTrue(square3.getBorderAt(Direction.WEST) instanceof Door);
		assertTrue(square1.getTemperature().equals(square2.getTemperature()));
		assertTrue(square1.getHumidity().compareTo(square2.getHumidity())==0);
		assertTrue(! (square2.getTemperature().equals(square3.getTemperature())));
		assertTrue(square2.getHumidity().compareTo(square3.getHumidity())!=0);
		
		door.interact();
		assertTrue((door instanceof Door) && (((Door)door).isOpen()));
		assertTrue(square1.getTemperature().equals(square2.getTemperature()));
		assertTrue(square1.getHumidity().compareTo(square2.getHumidity())==0);
		assertTrue(square2.getTemperature().equals(square3.getTemperature()));
		assertTrue(square2.getHumidity().compareTo(square3.getHumidity())==0);
		
		square2.removeNeighbourFrom(Direction.EAST);
		assertTrue(square1.getTemperature().equals(square2.getTemperature()));
		assertTrue(square1.getHumidity().compareTo(square2.getHumidity())==0);
		assertTrue(square3.isTerminated());
		assertTrue(square2.getNeighbourIn(Direction.EAST) == null);
	}
	@Test
	public void testTerminatedBorder() {
		Square square1 = new NormalSquare();
		Square square2 = new NormalSquare();
		
		Border door = new Door(false);
		square1.buildNewBorderIn(door, Direction.EAST);
		square1.setStateUsed();
		square2.setStateUsed();
		square2.setNeighbourInDirection(square1, Direction.WEST);
		assertTrue(door.hasAsSquare(square1));
		assertTrue(door.hasAsSquare(square2));
		
		door.terminate();
		assertTrue(door.isTerminated());
		assertFalse(door.hasSquare());
	}
	@Test
	public void testTerminatedBorderTerminate() {
		Square square1 = new NormalSquare();
		Square square2 = new NormalSquare(Temperature.CELSIUS_1, BigDecimal.TEN, Direction.CEILING);
		square1.setStateUsed();
		square2.setStateUsed();
		square2.setNeighbourInDirection(square1, Direction.CEILING);
		Border wall1 = square1.getBorderAt(Direction.FLOOR);
		assertTrue(square2.getBorderAt(Direction.CEILING) == wall1);
		assertTrue(wall1.hasAsSquare(square1));
		assertTrue(wall1.hasAsSquare(square2));
		
		wall1.terminate();
		assertTrue(wall1.isTerminated());
		assertFalse(wall1.hasSquare());
		assertFalse(square1.isTerminated());
		assertFalse(square2.isTerminated());
		Border wall2 = square1.getBorderAt(Direction.FLOOR);
		assertFalse(wall2.isTerminated());
		assertTrue(square2.getBorderAt(Direction.CEILING) == wall2);
		assertTrue(wall2.hasAsSquare(square1));
		assertTrue(wall2.hasAsSquare(square2));
		assertTrue(wall2 instanceof Wall);
		assertFalse(((Wall) wall2).isSlippery());
		assertFalse(wall1 == wall2);
	}
	
	//CONSTRUCTION OF NORMALSQUARES
	
	@Test
	public void constructor() {
		assertTrue(square1.getTemperature().equals(Temperature.CELSIUS_0));
		assertTrue(square1.getHumidity().compareTo(new BigDecimal(10)) == 0);
		assertTrue(square2.getTemperature().equals(Temperature.CELSIUS_0));
		assertTrue(square2.getHumidity().compareTo(new BigDecimal(50)) == 0);
		assertTrue(square1.isInitialised());
		assertTrue(square2.isInitialised());
		for(Direction direction: Direction.values()) {
			if(! (square1.getBorderAt(direction) instanceof Wall) &&
					(direction == Direction.FLOOR))
				assert false;
			if(! (square2.getBorderAt(direction) instanceof Wall) &&
					(direction == Direction.FLOOR))
				assert false;
			if(! (square1.getBorderAt(direction) instanceof Void) &&
					(direction != Direction.FLOOR))
				assert false;
			if(! (square2.getBorderAt(direction) instanceof Void) &&
					(direction != Direction.FLOOR))
				assert false;
		}
	}
	
	//BORDERS

	@Test
	public void canHaveAsBorders_Wall() {
		Map<Direction, Border> newBorders = new HashMap<Direction, Border>();
		for(Direction dir: Direction.values())
			newBorders.put(dir, new Wall(false));
		assertTrue(square1.canHaveAsBorders(newBorders));
		assertTrue(square2.canHaveAsBorders(newBorders));
		newBorders = new HashMap<Direction, Border>();
		for(Direction dir: Direction.values())
			newBorders.put(dir, new Wall(true));
		assertTrue(square1.canHaveAsBorders(newBorders));
		assertTrue(square2.canHaveAsBorders(newBorders));
		newBorders = new HashMap<Direction, Border>();
		newBorders.put(Direction.FLOOR, new Wall(false));
		assertTrue(square1.canHaveAsBorders(newBorders));
		assertTrue(square2.canHaveAsBorders(newBorders));
	}
	
	@Test
	public void canHaveAsBorders_DoorLegalCase() {
		Map<Direction, Border> newBorders = new HashMap<Direction, Border>();
		newBorders.put(Direction.WEST, new Door(false));
		assertTrue(square1.canHaveAsBorders(newBorders));
		assertTrue(square2.canHaveAsBorders(newBorders));
		newBorders = new HashMap<Direction, Border>();
		newBorders.put(Direction.EAST, new Door(true));
		assertTrue(square1.canHaveAsBorders(newBorders));
		assertTrue(square2.canHaveAsBorders(newBorders));
		newBorders = new HashMap<Direction, Border>();
		newBorders.put(Direction.EAST, new Door(false));
		newBorders.put(Direction.WEST, new Door(true));
		newBorders.put(Direction.CEILING, new Door(false));
		assertTrue(square1.canHaveAsBorders(newBorders));
		assertTrue(square2.canHaveAsBorders(newBorders));
	}
	
	@Test
	public void canHaveAsBorders_TooManyDoors() {
		Map<Direction, Border> newBorders = new HashMap<Direction, Border>();
		newBorders.put(Direction.WEST, new Door(false));
		newBorders.put(Direction.NORTH, new Door(false));
		newBorders.put(Direction.CEILING, new Door(false));
		newBorders.put(Direction.EAST, new Door(false));
		assertFalse(square1.canHaveAsBorders(newBorders));
		assertFalse(square2.canHaveAsBorders(newBorders));
	}
	
	@Test
	public void canHaveAsBorders_DoorInFloor() {
		Map<Direction, Border> newBorders = new HashMap<Direction, Border>();
		newBorders.put(Direction.FLOOR, new Door(false));
		assertFalse(square1.canHaveAsBorders(newBorders));
		assertFalse(square2.canHaveAsBorders(newBorders));
	}
	
	@Test
	public void canHaveAsBorders_VoidLegalCase() {
		Map<Direction, Border> newBorders = new HashMap<Direction, Border>();
		newBorders.put(Direction.SOUTH, new Void());
		newBorders.put(Direction.NORTH, new Void());
		newBorders.put(Direction.EAST, new Wall(false));
		newBorders.put(Direction.FLOOR, new Void());
		newBorders.put(Direction.WEST, new Void());
		newBorders.put(Direction.CEILING, new Void());
		assertTrue(square1.canHaveAsBorders(newBorders));
		assertTrue(square2.canHaveAsBorders(newBorders));
	}
	
	@Test
	public void canHaveAsBorders_VoidIllegalCase() {
		Map<Direction, Border> newBorders = new HashMap<Direction, Border>();
		newBorders.put(Direction.SOUTH, new Void());
		newBorders.put(Direction.NORTH, new Void());
		newBorders.put(Direction.EAST, new Void());
		newBorders.put(Direction.FLOOR, new Void());
		newBorders.put(Direction.WEST, new Void());
		newBorders.put(Direction.CEILING, new Void());
		assertFalse(square1.canHaveAsBorders(newBorders));
		assertFalse(square2.canHaveAsBorders(newBorders));
		newBorders = new HashMap<Direction, Border>();
		newBorders.put(Direction.FLOOR, new Void());
		assertFalse(square1.canHaveAsBorders(newBorders));
		assertFalse(square2.canHaveAsBorders(newBorders));
	}
	
	@Test
	public void canHaveAsBorders_Terminated() {
		Map<Direction, Border> newBorders = new HashMap<Direction, Border>();
		newBorders.put(Direction.WEST, new Door(false));
		square1.terminate();
		assertTrue(! square1.canHaveAsBorders(newBorders));
		newBorders = new HashMap<Direction, Border>();
		square1.terminate();
		assertTrue(square1.canHaveAsBorders(newBorders));
	}
}
